library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity cam_table is
port(
     clk,reset: in std_logic;
     D_addr, S_addr: in std_logic_vector(47 downto 0);
     inport_num: in std_logic_vector(1 downto 0);
	 outport_num: out std_logic_vector(1 downto 0);
	 complete,hit: out std_logic
	 );
end cam_table;

architecture a of cam_table is 
constant entry_num: natural :=32;
type table_type is array (0 to entry_num-1) of std_logic_vector(49 downto 0);
signal entry_reg: table_type;
signal D_addr_temp: std_logic_vector(47 downto 0);
signal S_addr_temp: std_logic_vector(47 downto 0);
signal hit_flag, complete_flag: std_logic;
signal outport: std_logic_vector(1 downto 0); 
--signal s_hit: std_logic; --source address hit
signal cam_full: std_logic; --indcate cam is full, enter aging process
signal aging_vector: std_logic_vector(31 downto 0); --vector used for indicating the adress tag which 

begin
--outside signal
process(clk,reset)
variable i: integer;
variable hit_tag: integer; --in learning process, indicating source address hit bit
variable next_tag: integer; --if source address not hit, record the next zero CAM address number
--variable cam_full_tag: integer :=0; --address tag if cam is full in aging process
variable s_hit:integer;
begin
     if (reset='1') then
                --for test only, assuming the cam originally has three addresses and ports
         entry_reg(0)<="00000000000000000000000000000000000000000000101010"; --10(2) <-update
--		 entry_reg(1)<="00000000000000000000000000000000000000000000111001"; --14(1) <-update
		 entry_reg(1)<="10010010001011011000101001101000000111100000000011"; --14(1)
		 entry_reg(2)<="00000000000000000000000000000000000000000001101011"; --26(3) <-update
		 entry_reg(3)<="00000000000000000000000000000000000000000000000101"; --1(1)  <-update
		 entry_reg(4)<="00000000000000000000000000000000000000000000111100"; --15(0) 
		 entry_reg(5)<="00000000000000000000000000000000000000000000110001"; --12(1) 
		 entry_reg(6)<="00000000000000000000000000000000000000000000100011"; --8(3)
		 entry_reg(7)<="00000000000000000000000000000000000000000000001110"; --3(2) 
		 entry_reg(8)<="00000000000000000000000000000000000000000000011000"; --6(0)
		 entry_reg(9)<="00000000000000000000000000000000000000000000011101"; --7(1)
		 entry_reg(10)<="00000000000000000000000000000000000000000000100111"; --9(3)
		 entry_reg(11)<="00000000000000000000000000000000000000000001101101"; --27(1)
		 entry_reg(12)<="00000000000000000000000000000000000000000000101100"; --11(0)
		 
		 entry_reg(13)<="00000000000000000000000000000000000000000000001010"; --2(2)
		 entry_reg(14)<="00000000000000000000000000000000000000000000010001"; --4(1)
		 entry_reg(15)<="00000000000000000000000000000000000000000000010111"; --5(3)
		 entry_reg(16)<="00000000000000000000000000000000000000000000110101"; --13(1) 
		 entry_reg(17)<="00000000000000000000000000000000000000000001000000"; --16(0)
		 entry_reg(18)<="00000000000000000000000000000000000000000001000101"; --17(1)
		 entry_reg(19)<="00000000000000000000000000000000000000000001001011"; --18(3)
		 entry_reg(20)<="00000000000000000000000000000000000000000001001110"; --19(2) 
		 entry_reg(21)<="00000000000000000000000000000000000000000001010000"; --20(0)
		 entry_reg(22)<="00000000000000000000000000000000000000000001010101"; --21(1)
		 entry_reg(23)<="00000000000000000000000000000000000000000001011011"; --22(3)
		 entry_reg(24)<="00000000000000000000000000000000000000000001011101"; --23(1)
		 entry_reg(25)<="00000000000000000000000000000000000000000001100000"; --24(0)
		 entry_reg(26)<="00000000000000000000000000000000000000000001100110"; --25(2)
		 entry_reg(27)<="00000000000000000000000000000000000000000001110001"; --28(1) 
		 entry_reg(28)<="00000000000000000000000000000000000000000001110111"; --29(3)
		 entry_reg(29)<="00000000000000000000000000000000000000000001111001"; --30(1)
		 entry_reg(30)<="00000000000000000000000000000000000000000001111100"; --31(0)
--		 entry_reg(31)<="00000000000000000000000000000000000000000010000001"; --32(1)

         entry_reg(31)<="00000000000000000000000000000000000000000000000000";

		 
         --for i in 0 to 31 loop
         --entry_reg(i)<=(others=>'0');
         --end loop;
         complete_flag<='0';
         hit_flag<='0';
         aging_vector<=(others=>'0');
--lookup process
     elsif (clk'event and clk='1') then
--for destination address lookup
     if (D_addr/="000000000000000000000000000000000000000000000000") then       
     for i in 0 to 31 loop
         if ((entry_reg(i)(49 downto 2)=D_addr) and (D_addr/="000000000000000000000000000000000000000000000000")) then
             hit_flag<='1';
             outport<=entry_reg(i)(1 downto 0);
             exit;
         else
             hit_flag<='0';
         end if;
     end loop;
		complete_flag<='1';
     else
		complete_flag<='0';
		hit_flag<='0';       
     end if;
--for source address lookup
     if (S_addr/="000000000000000000000000000000000000000000000000") then  
     for i in 0 to 31 loop
         if ((entry_reg(i)(49 downto 2)=S_addr) and (S_addr/="000000000000000000000000000000000000000000000000"))then
             s_hit:=1;
             hit_tag:=i;
             exit;
         else
             if ((entry_reg(i)(49 downto 2)/=S_addr) and (entry_reg(i)(49 downto 2)/="000000000000000000000000000000000000000000000000")) then
                 s_hit:=0;
                 if (i<=30) then
                     next_tag:=i+1;
                 else
                     cam_full<='1';
                 end if;
             end if;
         end if;
     end loop;
     
--learning process
          if (s_hit=1) then
              entry_reg(hit_tag)(1 downto 0)<=inport_num;
          else
              if (cam_full='0') then
                  entry_reg(next_tag)(49 downto 2)<=S_addr;
                  entry_reg(next_tag)(1 downto 0)<=inport_num;  
              else 
                  --aging process
                  if (aging_vector="11111111111111111111111111111111") then
                      aging_vector<=(others=>'0');
                  end if;
                  for i in 0 to 31 loop
                      if (aging_vector(i)='0') then
                          entry_reg(i)(49 downto 2)<=S_addr;
                          entry_reg(i)(1 downto 0)<=inport_num;
                          aging_vector(i)<='1'; --indicate that this adress tag has been updated
                          exit;
                      end if;
                  end loop;
              end if;
          end if;     
     end if;
     end if;
end process;
--output
hit<=hit_flag;
complete<=complete_flag;
outport_num<=outport;

end a;
